Crate mockall[−][src]
A powerful mock object library for Rust.
Mockall provides tools to create mock versions of almost any trait or struct. They can be used in unit tests as a stand-in for the real object.
Usage
There are two ways to use Mockall. The easiest is to use
#[automock]
. It can mock most traits, or structs
that only have a single impl
block. For things it can't handle, there is
mock!
.
Whichever method is used, the basic idea is the same.
- Create a mock struct. It's name will be the same as the original, with "Mock" prepended.
- In your test, instantiate the mock struct with its
new
ordefault
method. - Set expectations on the mock struct. Each expectation can have required
argument matchers, a required call count, and a required position in a
Sequence
. Each expectation must also have a return value. - Supply the mock object to the code that you're testing. It will return the preprogrammed return values supplied in the previous step. Any accesses contrary to your expectations will cause a panic.
User Guide
Getting started
Static Return values
Matching arguments
Call counts
Sequences
Checkpoints
Reference arguments
Reference return values
impl Trait
Mocking structs
Generic methods
Methods with generic lifetimes
Generic traits and structs
Associated types
Multiple and inherited traits
External traits
Static methods
Modules
Foreign functions
Async Traits
Crate features
Examples
Getting Started
use mockall::*; use mockall::predicate::*; #[automock] trait MyTrait { fn foo(&self, x: u32) -> u32; } fn call_with_four(x: &MyTrait) -> u32 { x.foo(4) } let mut mock = MockMyTrait::new(); mock.expect_foo() .with(predicate::eq(4)) .times(1) .returning(|x| x + 1); assert_eq!(5, call_with_four(&mock));
Static Return values
Every expectation must have an associated return value (though when the
nightly feature is enabled expectations will automatically return the
default values of their return types, if their return types implement
Default
.). For methods that return a static
value, the macros will
generate an Expectation
struct like
this
.
There are two ways to set such an expectation's return value: with a
constant
(return_const
)
or a closure
(returning
).
A closure will take the method's arguments by value.
#[automock] trait MyTrait { fn foo(&self) -> u32; fn bar(&self, x: u32, y: u32) -> u32; } let mut mock = MockMyTrait::new(); mock.expect_foo() .return_const(42u32); mock.expect_bar() .returning(|x, y| x + y);
Additionally, constants that aren't Clone
can be returned with the
return_once
method.
struct NonClone(); #[automock] trait Foo { fn foo(&self) -> NonClone; } let mut mock = MockFoo::new(); let r = NonClone{}; mock.expect_foo() .return_once(move || r);
return_once
can also be used for computing the return value with an
FnOnce
closure. This is useful for returning a non-Clone
value and also
triggering side effects at the same time.
fn do_something() {} struct NonClone(); #[automock] trait Foo { fn foo(&self) -> NonClone; } let mut mock = MockFoo::new(); let r = NonClone{}; mock.expect_foo() .return_once(move || { do_something(); r });
Mock objects are always Send
. If you need to use a return type that
isn't, you can use the
return_const_st
,
returning_st
,
or
return_once_st
,
methods. If you need to match arguments that are not Send
, you can use the
withf_st
These take a non-Send
object and add runtime access checks. The wrapped
object will be Send
, but accessing it from multiple threads will cause a
runtime panic.
#[automock] trait Foo { fn foo(&self, x: Rc<u32>) -> Rc<u32>; // Rc<u32> isn't Send } let mut mock = MockFoo::new(); let x = Rc::new(5); let argument = x.clone(); mock.expect_foo() .withf_st(move |x| *x == argument) .returning_st(move |_| Rc::new(42u32)); assert_eq!(42, *mock.foo(x));
Matching arguments
Optionally, expectations may have argument matchers set. A matcher will
verify that the expectation was called with the expected arguments, or panic
otherwise. A matcher is anything that implements the Predicate
trait.
For example:
#[automock] trait Foo { fn foo(&self, x: u32); } let mut mock = MockFoo::new(); mock.expect_foo() .with(eq(42)) .return_const(()); mock.foo(0); // Panics!
See predicate
for a list of Mockall's builtin predicate functions.
For convenience,
withf
is a shorthand for setting the commonly used
function
predicate. The arguments to the predicate function are the
method's arguments, by reference. For example:
#[automock] trait Foo { fn foo(&self, x: u32, y: u32); } let mut mock = MockFoo::new(); mock.expect_foo() .withf(|x: &u32, y: &u32| x == y) .return_const(()); mock.foo(2 + 2, 5); // Panics!
Matching multiple calls
Matchers can also be used to discriminate between different invocations of the same function. Used that way, they can provide different return values for different arguments. The way this works is that on a method call, all expectations set on a given method are evaluated in FIFO order. The first matching expectation is used. Only if none of the expectations match does Mockall panic. For example:
#[automock] trait Foo { fn foo(&self, x: u32) -> u32; } let mut mock = MockFoo::new(); mock.expect_foo() .with(eq(5)) .return_const(50u32); mock.expect_foo() .with(eq(6)) .return_const(60u32);
One common pattern is to use multiple expectations in order of decreasing specificity. The last expectation can provide a default or fallback value, and earlier ones can be more specific. For example:
#[automock] trait Foo { fn open(&self, path: String) -> Option<u32>; } let mut mock = MockFoo::new(); mock.expect_open() .with(eq(String::from("something.txt"))) .returning(|_| Some(5)); mock.expect_open() .return_const(None);
Call counts
By default, every expectation is allowed to be called an unlimited number of times. But Mockall can optionally verify that an expectation was called a fixed number of times, or any number of times within a given range.
#[automock] trait Foo { fn foo(&self, x: u32); } let mut mock = MockFoo::new(); mock.expect_foo() .times(1) .return_const(()); mock.foo(0); // Ok mock.foo(1); // Panics!
Sequences
By default expectations may be matched in any order. But it's possible to
specify the order by using a Sequence
. Any expectations may be added to
the same sequence. They don't even need to come from the same object.
#[automock] trait Foo { fn foo(&self); } let mut seq = Sequence::new(); let mut mock1 = MockFoo::new(); mock1.expect_foo() .times(1) .in_sequence(&mut seq) .returning(|| ()); let mut mock2 = MockFoo::new(); mock2.expect_foo() .times(1) .in_sequence(&mut seq) .returning(|| ()); mock2.foo(); // Panics! mock1.foo should've been called first.
Checkpoints
Sometimes its useful to validate all expectations mid-test, throw them away,
and add new ones. That's what checkpoints do. Every mock object has a
checkpoint
method. When called, it will immediately validate all methods'
expectations. So any expectations that haven't satisfied their call count
will panic. Afterwards, those expectations will be cleared so you can add
new expectations and keep testing.
#[automock] trait Foo { fn foo(&self); } let mut mock = MockFoo::new(); mock.expect_foo() .times(2) .returning(|| ()); mock.foo(); mock.checkpoint(); // Panics! foo hasn't yet been called twice.
#[automock] trait Foo { fn foo(&self); } let mut mock = MockFoo::new(); mock.expect_foo() .times(1) .returning(|| ()); mock.foo(); mock.checkpoint(); mock.foo(); // Panics! The expectation has been cleared.
Reference arguments
Mockall can mock methods with reference arguments, too. There's one catch:
the matcher Predicate
will take reference arguments by value, not by
reference.
#[automock] trait Foo { fn foo(&self, x: &u32) -> u32; } let mut mock = MockFoo::new(); let e = mock.expect_foo() // Note that x is a &u32, not a &&u32 .withf(|x: &u32| *x == 5) .returning(|x: &u32| *x + 1); assert_eq!(6, mock.foo(&5));
Reference return values
Mockall can also use reference return values. There is one restriction: the
lifetime of the returned reference must be either the same as the lifetime
of the mock object, or 'static
.
Mockall creates different expectation types for methods that return
references. Their API is the same as the basic Expectation
, except for
setting return values.
Methods that return 'static
references work just like methods that return
any other 'static
value.
struct Thing(u32); #[automock] trait Container { fn get(&self, i: u32) -> &'static Thing; } const THING: Thing = Thing(42); let mut mock = MockContainer::new(); mock.expect_get() .return_const(&THING); assert_eq!(42, mock.get(0).0);
Methods that take a &self
argument use an Expectation
class like
this,
which
gets its return value from the
return_const
method.
struct Thing(u32); #[automock] trait Container { fn get(&self, i: u32) -> &Thing; } let thing = Thing(42); let mut mock = MockContainer::new(); mock.expect_get() .return_const(thing); assert_eq!(42, mock.get(0).0);
Methods that take a &mut self
argument use an Expectation
class like
this,
class, regardless of whether the return value is actually mutable. They can
take their return value either from the
return_var
or
returning
methods.
struct Thing(u32); #[automock] trait Container { fn get_mut(&mut self, i: u32) -> &mut Thing; } let thing = Thing(42); let mut mock = MockContainer::new(); mock.expect_get_mut() .return_var(thing); mock.get_mut(0).0 = 43; assert_eq!(43, mock.get_mut(0).0);
Unsized types that are common targets for
Deref
are special. Mockall
will automatically use the type's owned form for the Expectation.
Currently, the
CStr
,
OsStr
,
Path
,
Slice
,
and
str
types are supported. Using this feature is automatic:
#[automock] trait Foo { fn name(&self) -> &str; } let mut mock = MockFoo::new(); mock.expect_name().return_const("abcd".to_owned()); assert_eq!("abcd", mock.name());
Similarly, Mockall will use a Boxed trait object for the Expectation of methods that return references to trait objects.
#[automock] trait Foo { fn name(&self) -> &dyn Display; } let mut mock = MockFoo::new(); mock.expect_name().return_const(Box::new("abcd")); assert_eq!("abcd", format!("{}", mock.name()));
Impl Trait
Rust 1.26.0 introduced the impl Trait
feature. It allows functions to
return concrete but unnamed types (and, less usefully, to take them as
arguments). It's almost the same as Box<dyn Trait>
but without the
extra allocation. Mockall supports deriving mocks for methods that return
impl Trait
, with limitations. When you derive the mock for such a method,
Mockall internally transforms the Expectation's return type to Box<dyn Trait>
, without changing the mock method's signature. So you can use it
like this:
struct Foo {} #[automock] impl Foo { fn foo(&self) -> impl Debug { // ... } } let mut mock = MockFoo::new(); mock.expect_foo() .returning(|| Box::new(String::from("Hello, World!"))); println!("{:?}", mock.foo());
However, impl Trait
isn't exactly equivalent to Box<dyn Trait>
but
with fewer allocations. There are some things the former can do but the
latter can't. For one thing, you can't build a trait object out of a
Sized
trait. So this won't work:
struct Foo {} #[automock] impl Foo { fn foo(&self) -> impl Clone { // ... } }
Nor can you create a trait object that implements two or more non-auto types. So this won't work either:
struct Foo {} #[automock] impl Foo { fn foo(&self) -> impl Debug + Display { // ... } }
For such cases, there is no magic bullet. The best way to mock methods like those would be to refactor them to return named (but possibly opaque) types instead.
See Also impl-trait-for-returning-complex-types-with-ease.html
impl Future
Rust 1.36.0 added the Future
trait. Unlike virtually every trait that
preceeded it, Box<dyn Future>
is mostly useless. Instead, you usually
need a Pin<Box<dyn Future>>
. So that's what Mockall will do when you mock
a method returning impl Future
or the related impl Stream
. Just
remember to use pin
in your expectations, like this:
struct Foo {} #[automock] impl Foo { fn foo(&self) -> impl Future<Output=i32> { // ... } } let mut mock = MockFoo::new(); mock.expect_foo() .returning(|| Box::pin(future::ready(42)));
Mocking structs
Mockall mocks structs as well as traits. The problem here is a namespace
problem: it's hard to supply the mock object to your code under test,
because it has a different name. The solution is to alter import paths
during test. The easiest way to do that is with the
mockall_double
crate.
#[automock]
works for structs that have a single impl
block:
use mockall_double::double; mod thing { use mockall::automock; pub struct Thing{} #[automock] impl Thing { pub fn foo(&self) -> u32 { // ... } } } #[double] use thing::Thing; fn do_stuff(thing: &Thing) -> u32 { thing.foo() } #[cfg(test)] mod t { use super::*; #[test] fn test_foo() { let mut mock = Thing::default(); mock.expect_foo().returning(|| 42); do_stuff(&mock); } }
For structs with more than one impl
block, see mock!
instead.
Generic methods
Generic methods can be mocked, too. Effectively each generic method is an
infinite set of regular methods, and each of those works just like any other
regular method. The expect_* method is generic, too, and usually must be
called with a turbofish. The only restrictions on mocking generic methods
are that all generic parameters must be 'static
, and generic lifetime
parameters are not allowed.
#[automock] trait Foo { fn foo<T: 'static>(&self, t: T) -> i32; } let mut mock = MockFoo::new(); mock.expect_foo::<i16>() .returning(|t| i32::from(t)); mock.expect_foo::<i8>() .returning(|t| -i32::from(t)); assert_eq!(5, mock.foo(5i16)); assert_eq!(-5, mock.foo(5i8));
Methods with generic lifetimes
A method with a lifetime parameter is technically a generic method, but
Mockall treats it like a non-generic method that must work for all possible
lifetimes. Mocking such a method is similar to mocking a non-generic
method, with a few additional restrictions. One restriction is that you
can't match calls with with
, you must use withf
instead. Another is
that the generic lifetime may not appear as part of the return type.
Finally, no method may have both generic lifetime parameters and generic
type parameters.
struct X<'a>(&'a i32); #[automock] trait Foo { fn foo<'a>(&self, x: X<'a>) -> i32; } let mut mock = MockFoo::new(); mock.expect_foo() .withf(|f| *f.0 == 5) .return_const(42); let x = X(&5); assert_eq!(42, mock.foo(x));
Generic traits and structs
Mocking generic structs and generic traits is not a problem. The mock
struct will be generic, too. The same restrictions apply as with mocking
generic methods: each generic parameter must be 'static
, and generic
lifetime parameters are not allowed.
#[automock] trait Foo<T: 'static> { fn foo(&self, t: T) -> i32; } let mut mock = MockFoo::<i16>::new(); mock.expect_foo() .returning(|t| i32::from(t)); assert_eq!(5, mock.foo(5i16));
Associated types
Traits with associated types can be mocked too. Unlike generic traits, the
mock struct will not be generic. Instead, you must specify the associated
types when defining the mock struct. They're specified as metaitems to the
#[automock]
attribute.
#[automock(type Key=u16; type Value=i32;)] pub trait A { type Key; type Value; fn foo(&self, k: Self::Key) -> Self::Value; } let mut mock = MockA::new(); mock.expect_foo() .returning(|x: u16| i32::from(x)); assert_eq!(4, mock.foo(4));
Multiple and inherited traits
Creating a mock struct that implements multiple traits, whether inherited or
not, requires using the mock!
macro. But once created,
using it is just the same as using any other mock object:
pub trait A { fn foo(&self); } pub trait B: A { fn bar(&self); } mock! { // Structure to mock C {} // First trait to implement on C impl A for C { fn foo(&self); } // Second trait to implement on C impl B for C { fn bar(&self); } } let mut mock = MockC::new(); mock.expect_foo().returning(|| ()); mock.expect_bar().returning(|| ()); mock.foo(); mock.bar();
External traits
Mockall can mock traits and structs defined in external crates that are
beyond your control, but you must use mock!
instead of
#[automock]
. Mock an external trait like this:
mock! { MyStruct {} // Name of the mock struct, less the "Mock" prefix impl Clone for MyStruct { // specification of the trait to mock fn clone(&self) -> Self; } } let mut mock1 = MockMyStruct::new(); let mock2 = MockMyStruct::new(); mock1.expect_clone() .return_once(move || mock2); let cloned = mock1.clone();
Static methods
Mockall can also mock static methods. But be careful! The expectations are
global. If you want to use a static method in multiple tests, you must
provide your own synchronization. For ordinary methods, expectations are
set on the mock object. But static methods don't have any mock object.
Instead, you must create a Context
object just to set their expectations.
#[automock] pub trait A { fn foo() -> u32; } let ctx = MockA::foo_context(); ctx.expect().returning(|| 99); assert_eq!(99, MockA::foo());
A common pattern is mocking a trait with a constructor method. In this case, you can easily set the mock constructor method to return a mock object.
struct Foo{} #[automock] impl Foo { fn from_i32(x: i32) -> Self { // ... } fn foo(&self) -> i32 { // ... } } let ctx = MockFoo::from_i32_context(); ctx.expect() .returning(|x| { let mut mock = MockFoo::default(); mock.expect_foo() .return_const(x); mock }); let foo = MockFoo::from_i32(42); assert_eq!(42, foo.foo());
Generic static methods
Mocking static methods of generic structs or traits, whether or not the methods themselves are generic, should work seamlessly.
#[automock] trait Foo<T: 'static> { fn new(t: T) -> MockFoo<T>; } let ctx = MockFoo::<u32>::new_context(); ctx.expect() .returning(|_| MockFoo::default()); let mock = MockFoo::<u32>::new(42u32);
Context checkpoints
The context object cleans up all expectations when it leaves scope. It also
has a checkpoint
method that functions just like a mock object's
checkpoint
method.
#[automock] pub trait A { fn foo() -> u32; } let ctx = MockA::foo_context(); ctx.expect() .times(1) .returning(|| 99); ctx.checkpoint(); // Panics!
A mock object's checkpoint method does not checkpoint static methods. This behavior is useful when using multiple mock objects at once. For example:
#[automock] pub trait A { fn build() -> Self; fn bar(&self) -> i32; } let ctx = MockA::build_context(); ctx.expect() .times(2) .returning(|| MockA::default()); let mut mock0 = MockA::build(); mock0.expect_bar().return_const(4); mock0.bar(); mock0.checkpoint(); // Does not checkpoint the build method let mock1 = MockA::build();
One more thing: Mockall normally creates a zero-argument new
method for
every mock struct. But it won't do that when mocking a struct that
already has a method named new
.
Modules
In addition to mocking types, Mockall can also derive mocks for
entire modules of Rust functions. Mockall will generate a new module named
"mock_xxx", if "xxx" is the original module's name. You can also use
#[double]
to selectively import the mock module.
mod outer { use mockall::automock; #[automock()] pub(super) mod inner { pub fn bar(x: u32) -> i64 { // ... } } } #[double] use outer::inner; #[cfg(test)] mod t { use super::*; #[test] fn test_foo_bar() { let ctx = inner::bar_context(); ctx.expect() .returning(|x| i64::from(x + 1)); assert_eq!(5, inner::bar(4)); } }
Foreign functions
One reason to mock modules is when working with foreign functions. Modules may contain foreign functions, even though structs and traits may not. Like static methods, the expectations are global.
mod outer { #[automock] pub mod ffi { extern "C" { pub fn foo(x: u32) -> i64; } } } #[double] use outer::ffi; fn do_stuff() -> i64 { unsafe{ ffi::foo(42) } } #[cfg(test)] mod t { use super::*; #[test] fn test_foo() { let ctx = ffi::foo_context(); ctx.expect() .returning(|x| i64::from(x + 1)); assert_eq!(43, do_stuff()); } }
Async Traits
Async traits aren't yet (as of 1.47.0) a part of the Rust language. But
they're available from the
async_trait
crate.
Mockall is compatible with this crate, with two important limitations:
-
The
#[automock]
attribute must appear before the#[async_trait]
attribute. -
The
#[async_trait]
macro must be imported with its canonical name.
// async_trait works with both #[automock] #[automock] #[async_trait] pub trait Foo { async fn foo(&self) -> u32; } // and mock! mock! { pub Bar {} #[async_trait] impl Foo for Bar { async fn foo(&self) -> u32; } }
Crate features
Mockall has a nightly feature. Currently this feature has three effects:
-
The compiler will produce better error messages.
-
Mocking modules will be enabled.
-
Expectations for methods whose return type implements
Default
needn't have their return values explicitly set. Instead, they will automatically return the default value.
With nightly enabled, you can omit the return value like this:
#[automock] trait Foo { fn foo(&self) -> Vec<u32>; } let mut mock = MockFoo::new(); mock.expect_foo(); assert!(mock.foo().is_empty());
Examples
For additional examples of Mockall in action, including detailed
documentation on the autogenerated methods, see
examples
.
Modules
examples | Examples of Mockall's generated code |
predicate | Predicate factories |
Macros
mock | Manually mock a structure. |
Structs
Sequence | Used to enforce that mock calls must happen in the sequence specified. |
Traits
Predicate | Trait for generically evaluating a type against a dynamically created predicate function. |
PredicateBooleanExt |
|
PredicateBoxExt |
|
PredicateFileContentExt |
|
PredicateStrExt |
|
Attribute Macros
automock | Automatically generate mock types for structs and traits. |